package com.googlecode.monitoring.db.dao;

import com.googlecode.monitoring.dao.AgentStatusDao;
import com.googlecode.monitoring.dao.XmlUtils;
import com.googlecode.monitoring.db.model.DbItemToCheck;
import com.googlecode.monitoring.db.model.TableOfData;
import com.googlecode.monitoring.exception.DataDaoException;
import com.googlecode.monitoring.model.ItemToCheck;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Repository;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.FileNotFoundException;
import java.util.*;

/**
 * Author: Sergiy Doroshenko
 * Date: Aug 25, 2010
 * Time: 2:54:18 PM
 */
@Repository
public class XmlDbAgentStatusDao implements AgentStatusDao {

    Logger log = Logger.getLogger(XmlDbAgentStatusDao.class);
    private Resource xmlDbStatusResource;
    private XmlUtils xmlUtils;
    private DbAgentXmlUtils dbAgentXmlUtils = new DbAgentXmlUtils();
    private SchedulerFactoryBean schedulerFactoryBean;

    public XmlUtils getXmlUtils() {
        return xmlUtils;
    }

    @Autowired
    public void setXmlUtils(XmlUtils xmlUtils) {
        this.xmlUtils = xmlUtils;
    }

    @Autowired
    public void setXmlDbStatusResource(Resource xmlDbStatusResource) {
        this.xmlDbStatusResource = xmlDbStatusResource;
    }    

    @Autowired(required = false)
    public void setSchedulerFactoryBean(SchedulerFactoryBean scheduler) {
        this.schedulerFactoryBean = scheduler;
    }

    @Override
    public ItemToCheck getItemFromCheckReport(ItemToCheck item) throws DataDaoException {
        try {
            Document doc = xmlUtils.getDocumentFromResource(xmlDbStatusResource, true);
            NodeList nodes = doc.getElementsByTagName("item");
            for (int i = 0; i < nodes.getLength(); i++) {
                Node xmlItem = nodes.item(i);
                if (xmlItem.getNodeType() == Node.ELEMENT_NODE) {
                    try {
                        Node tableNode = ((Element) xmlItem).getElementsByTagName("table").item(0);
                        if (((DbItemToCheck) item).getTable().equals(buildTableFromNode(tableNode))) {
                            return buildDataToCheckFromStatus((Element) xmlItem);
                        }
                    } catch (Exception e) {
                        log.error(xmlItem.toString() + ";i=" + i, e);
                        throw new DataDaoException(xmlItem.toString(), e);
                    }
                }
            }
            return null;
        } catch (Throwable e) {
            throw new DataDaoException(e);
        }
    }

	@Override
    public List<ItemToCheck> getAllItemsFromCheckReport() throws DataDaoException, FileNotFoundException {
        return getXmlDataItemsFromStatus();
    }
    
    @Override
    public boolean saveItemToCheckReport(ItemToCheck item) {
        try {
            Set<ItemToCheck> reportItems;
            try {
                reportItems = new HashSet<ItemToCheck>(getAllItemsFromCheckReport());
            }catch (FileNotFoundException e) {
                reportItems = new HashSet<ItemToCheck>(1);
            }
            reportItems.remove(item);
            reportItems.add(item);
            saveDbItemsToCheckReport(reportItems);
            return true;
        }
        catch (Exception e) {
            log.error(" error with saving item = " + item,e);
            throw new DataDaoException(" error with saving item = " + item,e);
        }                
    }

	public Resource getStatusResource() {
		return xmlDbStatusResource;
	}

    private void saveDbItemsToCheckReport(Collection<ItemToCheck> reportItems) throws Exception {
        Document doc = xmlUtils.createDoc();
        Element root = (Element) doc.getElementsByTagName("document").item(0);
        for (ItemToCheck item : reportItems) {
            root.appendChild(dbAgentXmlUtils.buildDbItemToCheckElement(doc,(DbItemToCheck)item,schedulerFactoryBean));
        }
        xmlUtils.saveDocument(doc, xmlDbStatusResource);
    }

    private TableOfData buildTableFromNode(Node tableNode) {
        TableOfData tableOfData = new TableOfData();
        if (tableNode.getAttributes().getNamedItem("name") != null) {
            tableOfData.setName(tableNode.getAttributes().getNamedItem("name").getNodeValue());
        }
        if (tableNode.getAttributes().getNamedItem("columnName") != null) {
            tableOfData.setColumnName(tableNode.getAttributes().getNamedItem("columnName").getNodeValue());
        }
        return tableOfData;
    }

    private DbItemToCheck buildDataToCheckFromStatus(Element xmlItemElmnt) {
        DbItemToCheck dbItem = dbAgentXmlUtils.buildDataToCheck(xmlItemElmnt,null);
        xmlUtils.fillStateAndTimeToItem(xmlItemElmnt, dbItem);
        return dbItem;
    }   

    private List<ItemToCheck> getXmlDataItemsFromStatus() throws DataDaoException {
        try {
            Document doc = xmlUtils.getDocumentFromResource(xmlDbStatusResource);
            NodeList nodes = doc.getElementsByTagName("item");
            List<ItemToCheck> items = new ArrayList<ItemToCheck>(nodes.getLength());
            log.debug("readed " + nodes.getLength() + " nodes item");
            for (int i = 0; i < nodes.getLength(); i++) {
                Node xmlItem = nodes.item(i);
                if (xmlItem.getNodeType() == Node.ELEMENT_NODE) {
                    try {
                        items.add(buildDataToCheckFromStatus((Element) xmlItem));
                    } catch (Exception e) {
                        log.error(xmlItem.toString() + ";i=" + i, e);
                        throw new DataDaoException(xmlItem.toString(), e);
                    }
                }
            }
            return items;
        } catch (Throwable e) {
            throw new DataDaoException(e);
        }
    }
    
}
